﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Helpers;
using System.Web.WebPages;
using System.Web.WebPages.Scope;

/// <summary>
/// Groupon Helper for Microsoft WebMatrix
/// For more information on the data returned by the helper see http://sites.google.com/site/grouponapiv2/home
/// </summary>
public static class Groupon {
    private const string InitializedExceptionMessage = "The Groupon Helper has not been initialized. You should call the Initialize method.";
    private const string ArgumentNullExceptionMessage = "Argument cannot be null or empty.";
    private const string GetDealsNullParametersExceptionMessage = "You should either specify the divisionId or the latitude/longitude parameters.";

    private const string GetDivisionsUrl = "http://api.groupon.com/v2/divisions.json";
    private const string GetDealsUrl = "http://api.groupon.com/v2/deals.json";
    private const string GetDealUrl = "http://api.groupon.com/v2/deals/{0}.json?{1}";

    private static readonly object _apiKey = new object();
    private static readonly object _referralCodeKey = new object();
    private static readonly object _initializedKey = new object();

    public static string ApiKey {
        get {
            if (!Initialized) {
                throw new InvalidOperationException(InitializedExceptionMessage);
            }
            return (string)(ScopeStorage.CurrentScope[_apiKey] ?? "");
        }

        private set {
            if (value == null) {
                throw new ArgumentNullException("ApiKey");
            }

            ScopeStorage.CurrentScope[_apiKey] = value;
        }
    }

    public static string ReferralCode
    {
        get {
            return (string)(ScopeStorage.CurrentScope[_referralCodeKey] ?? "");
        }

        private set {
            ScopeStorage.CurrentScope[_referralCodeKey] = value;
        }
    }

    private static bool Initialized {
        get {
            return (bool)(ScopeStorage.CurrentScope[_initializedKey] ?? false);
        }
        set {
            ScopeStorage.CurrentScope[_initializedKey] = value;
        }
    }

    /// <summary>
    /// Initializes the Groupon helper.
    /// </summary>
    /// <param name="apiKey">Your Groupon API Key.</param>
    /// <param name="referralCode">Your Referral Code.</param>
    public static void Initialize(string apiKey, string referralCode = "") {
        if (apiKey.IsEmpty()) {
            throw new ArgumentException(ArgumentNullExceptionMessage, "apiKey");
        }

        ApiKey = apiKey;
        ReferralCode = referralCode;

        Initialized = true;
    }

    /// <summary>
    /// Displays a widget with the daily Groupon's deal.
    /// </summary>
    /// <param name="linkColor">RGB color for the deal hyperlink.</param>
    /// <param name="textColor">RGB color for the buy-countdown text.</param>
    /// <param name="bakgroundColor">RGB color for the widget background text.</param>
    /// <param name="titleColor">RGB color for the deal title.</param>
    /// <param name="getItButtonColor">RGB color for the "Get It" button.</param>
    /// <param name="priceTagColor">RGB color for the price tage.</param>
    /// <param name="roundedCorners">Determines if the widget is rendered with rounded borders or not (default: true).</param>
    /// <param name="city">The city for wich to get the deal. If not specified, will be auto-detected based on the browser info.</param>    
    public static HtmlString GetDailyDealWidget(
                                    string linkColor = "#0981b3",
                                    string textColor = "#000000",
                                    string bakgroundColor = "#7fb93c",
                                    string titleColor = "#ffffff",
                                    string getItButtonColor = "#7fb93c",
                                    string priceTagColor = "#67d6f2",
                                    bool roundedCorners = true,
                                    string city = "") {

        var widgetOptions = new Dictionary<string, object>();

        widgetOptions["theme"] = new {
            deal = new { link_color = linkColor },
            shell = new { color = textColor, background = bakgroundColor },
            header = new { color = titleColor },
            buttons = new { get_it_btn = new { background = getItButtonColor }, price_tag_btn = new { background = priceTagColor }},
            rounded = roundedCorners
        };

        if (!city.IsEmpty()) {
            widgetOptions["city"] = city;
        }

        if (!ReferralCode.IsEmpty()) {
            widgetOptions["referral_code"] = ReferralCode;
        }

        return new HtmlString(String.Format(CultureInfo.InvariantCulture,
                                            @"<script type='text/javascript' src='http://groupon.com/javascripts/common/widget.js'></script><script type='text/javascript'>new GRPN.Widget({0}).render();</script>",
                                            Json.Encode(widgetOptions)));
    }

    /// <summary>
    /// Returns a list of all launched divisions.
    /// </summary>
    /// <param name="show">Determines which fields of the divisions should be returned. Possible values: default | all | {coma separated fields list}.</param>
    public static IList<dynamic> GetDivisions(string show = "default") {

        var queryStringValues = new NameValueCollection();
        queryStringValues.Add("client_id", ApiKey);        
        queryStringValues.Add("show", show);

        var queryString = BuildQueryString(queryStringValues);

        var url = string.Format(CultureInfo.InvariantCulture, "{0}?{1}", GetDivisionsUrl, queryString);

        var jsonResponse = new WebClient().DownloadString(url);               
        var response = Json.Decode(jsonResponse);

        if (response.Error != null) {
            throw new InvalidOperationException(response.Message.Error);
        }

        return new List<dynamic>(response.divisions);
    }

    /// <summary>
    /// Returns an ordered list of deals that are currently launched for a specific division.
    /// </summary>
    /// <param name="divisionId">The id of the division for which to get deals.</param>
    /// <param name="latitude">Used for locating deals near a specific latitude / longitude.</param>
    /// <param name="longitude">Used for locating deals near a specific latitude / longitude.</param>
    /// <param name="radius">Distance (in miles) from the provided lat / lng within which to locate deals.</param>
    /// <param name="area">The id of the area for which to get deals. For example: GetDeals(divisionId: "san-francisco", area: "east-bay")</param>
    /// <param name="show">Determines which fields of the deals should be returned. Possible values: default | all | {coma separated fields list}</param>    
    public static IList<dynamic> GetDeals(string divisionId = "", string latitude = "", string longitude = "", string radius = "", string area = "", string show = "default") {

        if (divisionId.IsEmpty() && (latitude.IsEmpty() || longitude.IsEmpty())) {
            throw new ArgumentException(GetDealsNullParametersExceptionMessage);
        }

        var queryStringValues = new NameValueCollection();
        queryStringValues.Add("client_id", ApiKey);
        queryStringValues.Add("division_id", divisionId);        
        queryStringValues.Add("lat", latitude);
        queryStringValues.Add("lng", longitude);
        queryStringValues.Add("radius", radius);
        queryStringValues.Add("area", area);
        queryStringValues.Add("show", show);

        var queryString = BuildQueryString(queryStringValues);
        var requestUrl = string.Format(CultureInfo.InvariantCulture, "{0}?{1}", GetDealsUrl, queryString);

        var jsonResponse = new WebClient().DownloadString(requestUrl);
        var response = Json.Decode(jsonResponse);

        if (response.Error != null) {
            throw new InvalidOperationException(response.Error.Message);
        }

        return new List<dynamic>(response.deals);
    }

    /// <summary>
    /// Shows detailed information about a specified deal.
    /// </summary>
    /// <param name="dealId">The deal id.</param>
    /// <param name="show">Determines which fields of the deals should be returned. Possible values: default | all | {coma separated fields list}</param>    
    public static dynamic GetDeal(string dealId, string show = "default") {

        var queryStringValues = new NameValueCollection();
        queryStringValues.Add("client_id", ApiKey);
        queryStringValues.Add("show", show);
        queryStringValues.Add("referral_id", ReferralCode);       

        var queryString = BuildQueryString(queryStringValues);
        var requestUrl = string.Format(CultureInfo.InvariantCulture, GetDealUrl, dealId, queryString);

        var jsonResponse = new WebClient().DownloadString(requestUrl);
        var response = Json.Decode(jsonResponse);

        if (response.Error != null) {
            throw new InvalidOperationException(response.Error.Message);
        }

        return response.deal;
    }

    private static string BuildQueryString(NameValueCollection parameters) {
        NameValueCollection queryString = HttpUtility.ParseQueryString("");

        foreach (var key in parameters.AllKeys) {
            if (!parameters[key].IsEmpty()) {
                queryString.Add(key, parameters[key]);
            }
        }

        return queryString.ToString();
    }
}